/************************************************************************/
/*  									*/
/*  stdvq.c								*/
/*									*/
/*  Tom Lookabaugh							*/
/*  August 13, 1987							*/
/*  Last Revision: January 6, 1989					*/
/*									*/
/*  DESCRIPTION								*/
/*	Main program for generalized Lloyd algorithm design.		*/
/*	Reads command line, gets memory, opens files, initialized size	*/
/*	one codebook, enlarges codebook by splitting, and runs 		*/
/*	generalized Lloyd algorithm until convergence.			*/
/*  CALLS								*/
/*	initialize(), lloyd(), error(), splitcdwds(), writecdbk()	*/
/*  OPTIONS								*/
/*	-t	training sequence file name	train.dat		*/
/*	-c 	codebook file name		cdbk.dat		*/
/*	-d	data vector dimension		8			*/
/*	-f 	final codebook size		256			*/
/*	-h	convergence threshold		0.01			*/
/*	-a	codeword split additive offset	0.01			*/
/*	-m	codeword split mult. offset	0.01			*/
/*	-i	intial codebook size		0			*/
/*		  (if non-zero, must also have an initial codebook)	*/
/*		  (should be a power of 2)				*/
/*	-b	initial codebook file name	initcdbk.dat		*/
/*	-w	<no argument> write codebook	not set			*/
/*		  at every increase in size				*/
/*  NOTE								*/
/*	The size of cdbk and cdbkup (codebook and update information	*/
/*	for centroids) may be changed relative to trvect (training	*/
/*	sequence vector) dimension at compile time.  This is done by	*/
/*	using the compiler option -D to define CDBKADD (the difference	*/
/*	between trvect and cdbk dimensions) and CDBKUPADD (the		*/
/*	difference between trvect and cdbkup dimensions). Normally,	*/
/*	these are zero, i.e., -DCDBKADD=0 -DCDBKUPADD=0.		*/
/*									*/
/************************************************************************/

#include "stdvq.h"
#include <math.h>

#include "stdvq.defs"

main (argc, argv)

    int argc;
    char *argv[];

{

    char trsqfilename[50];	/* training sequence file name		*/
    char cdbkfilename[50];	/* codebook file name			*/
    char initcdbkfilename[50];	/* initial codebook file name		*/
    FILE *initcdbkfile;		/* initial codebook file		*/
    int fincdbksz;		/* final codebook size in vectors	*/
    int i;			/* iteration counter			*/
    double *cdbk;		/* codebook				*/
    double *cdbkup;		/* update information for centroids	*/
    long *count;		/* cell populations			*/
    DATATYPE *trvectin;		/* training sequence vector (input)	*/
    double *trvect;		/* training sequence vector		*/
    int cdbksz2;		/* log2 of codebook size in vectors	*/
    int temp_int;		/* temporary storage			*/
    double dist;		/* distortion				*/
    int cdbksz;			/* current codebook size in vectors	*/
    int new_cdwds;		/* number of new codewords to make	*/
    double threshold;		/* convergence threshold		*/
    int write_all;		/* write codebook every time flag	*/
    char tempcdbkfilename[50];	/* temporary codebook file name		*/
    char passnum[10];		/* codebook size in characters		*/
    int init_cdbksz;		/* initial codebook size		*/
    int init_cdbksz2;		/* log2 of initial codebook size	*/

    
/* Set defaults.							*/    

    strcpy(trsqfilename, DEF_trsqfilename);
    strcpy(cdbkfilename, DEF_cdbkfilename);
    strcpy(initcdbkfilename, "initcdbk.dat");
    fincdbksz = DEF_fincdbksz;
    threshold = DEF_threshold;
    dim = DEF_dim;
    add_offset = DEF_add_offset;
    mult_offset = DEF_mult_offset;
    write_all = DEF_write_all;
    init_cdbksz = 0;


    
/* Read and interpret command line arguments. */    

    i = 1;                                       
        while (i < argc) {                               
        if (*(argv[i]) != '-') error("bad arguments (stdvq 1)");
        else {
            switch(*(argv[i++] + 1)) {
                case 't':
                    strcpy(trsqfilename, argv[i]);
                    break;
                case 'c':
                    strcpy(cdbkfilename, argv[i]);
                    break;
                case 'b':
                    strcpy(initcdbkfilename, argv[i]);
                    break;
                case 'f':
                    sscanf(argv[i], "%d", &fincdbksz);
                    break;                       
                case 'i':
                    sscanf(argv[i], "%d", &init_cdbksz);
                    break;                       
                case 'h':
                    sscanf(argv[i], "%lf", &threshold);
                    break;                       
                case 'd':
                    sscanf(argv[i], "%d", &dim);
                    break;                       
                case 'a':
                    sscanf(argv[i], "%lf", &add_offset);
                    break;
                case 'm':
                    sscanf(argv[i], "%lf", &mult_offset);
                    break;
                case 'w':
                    write_all = 1;
                    i--;
                    break;                                        
                default:
                    error("bad arguments (stdvq 2)");
            }
            i++;
        }
    }
    
/* Get memory. */

    cdbkdim = dim + CDBKADD;
    cdbkupdim = dim + CDBKUPADD;
    if (((cdbk = (double *)calloc(cdbkdim*fincdbksz, sizeof(double))) == NULL)
      || 
      ((cdbkup = (double *)calloc(cdbkupdim*fincdbksz, sizeof(double)))
      == NULL)
      || ((count = (long *)calloc(fincdbksz, sizeof(long))) == NULL)
      || ((trvect = (double *)calloc(dim, sizeof(double))) == NULL) 
      || ((trvectin = (DATATYPE *)calloc(dim, sizeof(DATATYPE))) == NULL))
      error("insufficient memory available (stdvq 3)");

    
/* Open necessary files. */
    
    if ((trsqfile = fopen(trsqfilename, "r")) == NULL)
      error("couldn't open training sequence file (stdvq 4)");
    if (!write_all) if ((cdbkfile = fopen(cdbkfilename, "w")) == NULL)
      error("couldn't open codebook file (stdvq 5)");

/* Print status to stdout. */

    printf("\n >> stdvq status: << \n\n");
    printf("training sequence file name: %s \n", trsqfilename);
    printf("codebook file name: %s \n", cdbkfilename);
    printf("vector dimension: %d \n", dim);
    printf("final codebook size: %d \n", fincdbksz);
    printf("convergence threshold: %lf \n", threshold);
    printf("additive offset: %lf \n", add_offset);
    printf("multiplicative offset: %lf \n\n", mult_offset);
    if (init_cdbksz > 0) {
      printf("initial codebook file name: %s \n", initcdbkfilename);
      printf("inital codebook size: %d \n", init_cdbksz);
    }
    fflush(stdout);
  
/* Calculate doublings of codebook size.				*/

    cdbksz2 = 0;
    temp_int = 1;
    while (temp_int < fincdbksz) {
        cdbksz2++;
        temp_int += temp_int;
    }  

    if (init_cdbksz > 0) {
        init_cdbksz2 = 0;
        temp_int = 1;
        while (temp_int < init_cdbksz) {
            init_cdbksz2++;
            temp_int += temp_int;
        }
    } 
    else init_cdbksz2 = -1; 

/* Initialize (calculate centroid of entire training sequence).		*/
    
    if (init_cdbksz2 == -1) {   
        dist = initialize(cdbk, cdbkup, trvect, trvectin);
        printf("Centroid Distortion: %lf\n", dist);
        cdbksz = 1;
        init_cdbksz2 = 0;
    
/* If all codebooks are being written, write centroid codebook.		*/

        if (write_all) {
	    strcpy(tempcdbkfilename, cdbkfilename);
	    strcat(tempcdbkfilename, "1");
	    if ((cdbkfile = fopen(tempcdbkfilename, "w")) == NULL) 
	      error("couldn't open codebook file (stdvq 6)");
	    writecdbk(cdbk, cdbksz, cdbkup);
	    fclose(cdbkfile);
        }
    }
    else {
        cdbksz = init_cdbksz;
	if ((initcdbkfile = fopen(initcdbkfilename, "r")) == NULL)
	  error ("couldn't open initial codebook file (stdvq 6a)");
	if (fread(cdbk, sizeof(double), init_cdbksz*cdbkdim, initcdbkfile)
	  != init_cdbksz*cdbkdim) error("failed to read inital codebook");
	for (i = 0; i < init_cdbksz*cdbkdim; cdbkup[i] = cdbk[i], i++);
    }
    
/* Double codebook size and run Lloyd algorithm until large enough.	*/

    for (i = init_cdbksz2; i < cdbksz2; i++) {
    
	new_cdwds = ((fincdbksz - 2*cdbksz) >= 0) ? cdbksz : fincdbksz - cdbksz;
        splitcdwds(cdbk, cdbkup, cdbksz, new_cdwds);
        cdbksz += new_cdwds;
      
        dist = lloyd (cdbk, cdbkup, count, trvect, trvectin, cdbksz, threshold);

        printf("Codebook Size %d Distortion %lf\n", cdbksz, dist);
        
        if (write_all) {
	    strcpy(tempcdbkfilename, cdbkfilename);
	    sprintf(passnum, "%d", cdbksz);
	    strcat(tempcdbkfilename, passnum);
	    if ((cdbkfile = fopen(tempcdbkfilename, "w")) == NULL) 
	      error("couldn't open codebook file (stdvq 7)");
	    writecdbk(cdbk, cdbksz, cdbkup);
	    fclose(cdbkfile);
	}
        
    }
    
/* Write codebook file.							*/

    if (!write_all) writecdbk(cdbk, cdbksz, cdbkup);
      
}

